module heard;

import dxml.dom;
import std.file:readText;

import std.json;
import jsonwrap;
//import arsd.jsvar;
//import arsd.script;

import std.logger;
import std.stdio:writeln,writefln,toFile;
import std.string:format;
import std.conv:to;

import std.bigint:BigInt;

import std.algorithm:sort;


enum devicePath
{
    /// 目标名
    targetName = "/device/name",
    cpu = "/device/cpu",
    /// 地址单元位宽
    addressUnitBits = "/device/addressUnitBits",
    /// 最大可访问位宽
    maxAccessBits = "/device/width",
    /// 外设
    peripherals = "/device/peripherals/peripheral",
    
}

struct CpuInfo
{
    string name;
    string revision;
    string endian;
    string mpuPresent;
    string fpuPresent;
    string fpuDP;
    string dspPresent;
    string icachePresent;
    string dcachePresent;
    string vtorPresent;
    string nvicPrioBits;
    string vendorSystickConfig;
    string deviceNumInterrupts;
    string sauNumRegions;
}
struct suaRegion
{

}


void makeHeader(string inputFile, string outputFile)
{

    JSONValue svdJson = parseJSON(readText(inputFile));

    if(!svdJson.exists(devicePath.peripherals))
    {
        errorf("peripherals not found in json file");
        return;
    }

    string outString = "";

    auto periJson = svdJson.read!JSONValue(devicePath.peripherals);
    /// 访问宽度
    
    int accessBits = svdJson.read!string(devicePath.maxAccessBits).BigInt().toInt();

    /// 寄存器
    Interrupt[]    irqMap = getIrqMap(periJson);
    /// 外设
    Peripheral[]    peripherals = getPeripherals(periJson);


    string headerIrqs =  makeIrqs(irqMap);

    string headerPeripherals = makePeripherals(peripherals,accessBits);

    outString ~= "/*\n";
    outString ~= " auto-generated by makeHeader.d\n";
    outString ~= " Do not modify it.\n";
    outString ~= "*/\n\n";    
    outString ~= "import chip.util;\n";

    outString ~= "extern(C++)\nfinal abstract class Peripherals {\n";
    outString ~= headerIrqs;
    outString ~= headerPeripherals;
    outString ~= "}\n";

    outString.toFile(outputFile);


    //writeln(headerIrqs);
}

/// 获取外设
Peripheral[] getPeripherals(JSONValue svdJson)
in(svdJson.type == JSONType.array)
{
    Register[] mReadReg(JSONType regJson)
    {
        return null;
    }

    Peripheral[] peripherals;

    foreach(JSONValue peri;svdJson.array)
    {
        Peripheral p;
        p.name = peri.read!string("name");
        p.description = peri.safe!string("description","");
        p.groupName = peri.safe!string("groupName","");
        p.baseAddress = peri.read!string("baseAddress");



        // addressBlock
        if(peri["addressBlock"].type == JSONType.array)
        {
            foreach (reg; peri["addressBlock"].array)
            {
                AddressBlock block;
                block.offset = reg.safe!string("offset","");
                block.size = reg.safe!string("size","");
                block.usage = reg.safe!string("usage","");
                block.protection = reg.safe!string("protection","");
                p.addressBlocks ~= block;
            }
        }
        else
        {

            AddressBlock block;
            block.offset = peri.safe!string("addressBlock/offset","");
            block.size = peri.safe!string("addressBlock/size","");
            block.usage = peri.safe!string("addressBlock/usage","");
            block.protection = peri.safe!string("addressBlock/protection","");
            p.addressBlocks ~= block;
        }

        // interrupt
        if("interrupt" in peri){
            if(peri["interrupt"].type == JSONType.array)
            {
                foreach (irq; peri["interrupt"].array)
                {
                    Interrupt mirq;
                    mirq.name = irq.read!string("name");
                    mirq.description = irq.safe!string("description","");
                    mirq.value = irq.read!string("value");
                    p.interrupts ~= mirq;
                }
            }
            else
            {
                Interrupt mirq;
                mirq.name = peri.safe!string("interrupt/name","");
                mirq.description = peri.safe!string("interrupt/description","");
                mirq.value = peri.read!string("interrupt/value");
                p.interrupts ~= mirq;
            }

        }

        // register
        {
            if(peri.exists("registers/register"))
            {
                JSONValue selectJson = peri.read!JSONValue("registers/register");
                if(selectJson.type == JSONType.array)
                {
                    foreach (reg; selectJson.array)
                    {
                        Register mreg;
                        mreg.access = reg.safe!string("access","");
                        mreg.addressOffset = reg.safe!string("addressOffset","");
                        mreg.description = reg.safe!string("description","");
                        mreg.displayName = reg.safe!string("displayName","");
                        mreg.name = reg.safe!string("name","");
                        mreg.size = reg.safe!string("size","");
                        mreg.resetValue = reg.safe!string("resetValue","");
                        
                        /// 位域
                        if(reg.exists("fields/field")){
                            JSONValue fieldJson = reg.read!JSONValue("fields/field");
                            if(fieldJson.type == JSONType.array)
                            {
                                foreach (field; fieldJson.array)
                                {
                                    BitField mfield;
                                    mfield.name = field.safe!string("name","");
                                    mfield.description = field.safe!string("description","");
                                    mfield.bitOffset = field.safe!string("bitOffset","");
                                    mfield.bitWidth = field.safe!string("bitWidth","");
                                    mfield.access = field.safe!string("access","");
                                    mreg.bitFields ~= mfield;
                                }
                            }else{
                                
                                BitField mfield;
                                mfield.name = fieldJson.safe!string("name","");
                                mfield.description = fieldJson.safe!string("description","");
                                mfield.bitOffset = fieldJson.safe!string("bitOffset","");
                                mfield.bitWidth = fieldJson.safe!string("bitWidth","");
                                mfield.access = fieldJson.safe!string("access","");
                                mreg.bitFields ~= mfield;
                            }
                        }

                        p.registers ~= mreg;
                    }
                }else{
                    Register mreg;
                    mreg.access = selectJson.safe!string("access","");
                    mreg.addressOffset = selectJson.safe!string("addressOffset","");
                    mreg.description = selectJson.safe!string("description","");
                    mreg.displayName = selectJson.safe!string("displayName","");
                    mreg.name = selectJson.safe!string("name","");
                    mreg.size = selectJson.safe!string("size","");
                    mreg.resetValue = selectJson.safe!string("resetValue","");

                    /// 位域
                    if(selectJson.exists("fields/field")){
                        JSONValue fieldJson = selectJson.read!JSONValue("fields/field");
                        if(fieldJson.type == JSONType.array)
                        {
                            foreach (field; fieldJson.array)
                            {
                                BitField mfield;
                                mfield.name = field.safe!string("name","");
                                mfield.description = field.safe!string("description","");
                                mfield.bitOffset = field.safe!string("bitOffset","");
                                mfield.bitWidth = field.safe!string("bitWidth","");
                                mfield.access = field.safe!string("access","");
                                mreg.bitFields ~= mfield;
                            }
                        }else{
                            
                            BitField mfield;
                            mfield.name = fieldJson.safe!string("name","");
                            mfield.description = fieldJson.safe!string("description","");
                            mfield.bitOffset = fieldJson.safe!string("bitOffset","");
                            mfield.bitWidth = fieldJson.safe!string("bitWidth","");
                            mfield.access = fieldJson.safe!string("access","");
                            mreg.bitFields ~= mfield;
                        }
                    }

                    p.registers ~= mreg;
                }
            }

        }


        //p.interrupts = getInterrupts(peri);
        //p.registers = getRegisters(peri);
        peripherals ~= p;
    }
    return peripherals;
}

/// 读取外设信息中的中断信息
Interrupt[] getIrqMap(JSONValue svdJson)
in(svdJson.type == JSONType.array)
{
    Interrupt[] irqMap;

    bool hasIrq (string i)
    {
        foreach (irq; irqMap)
        {
            if(irq.value == i)
            {
                return true;
            }
        }
        return false;
    }


    foreach(JSONValue irqs;svdJson.array)
    {
        if("interrupt" in irqs){
            if(irqs["interrupt"].type == JSONType.array)
            {
                foreach(item;irqs["interrupt"].array)
                {
                    if(hasIrq(item.read!string("value"))) continue;

                    Interrupt irq;
                    irq.name = item.read!string("name");
                    irq.description = item.safe!string("description","");
                    irq.value = item.read!string("value");
                    irqMap ~= irq;
                }
            }else{
                Interrupt irq;
                if(hasIrq(irqs.read!string("/interrupt/value"))) continue;
                irq.name = irqs.read!string("/interrupt/name");
                irq.description = irqs.safe!string("/interrupt/description","");
                irq.value = irqs.read!string("/interrupt/value");
                irqMap ~= irq;
            }
        }
    }

    //writeln("irqMap:",irqMap);
    return irqMap;
}

/// 建立外设段落
string makePeripherals(Peripheral[] peripherals,uint width=32)
in(peripherals.length > 0)
{
    string peripheralsStr;

    string[string] groupStr;

    //peripheralsStr ~= "extern(C++)\n";

    /// 生成调用指针`enum DBG_Type* DBG = MMIO!(0xE0042000,DBG_Type);`
    foreach (item; peripherals)
    {
        //peripheralsStr ~= format("\t// %s\n",item.description);
        peripheralsStr ~= format(
            "\tenum %s_Type* %s = MMIO!(%s, %s_Type);\t//%s\n", 
            item.name, 
            item.name, 
            item.baseAddress, 
            item.name,
            item.description
            );
    }

    /// 生成寄存器段落 
    foreach (item; peripherals)
    {
        string mstr;
        mstr ~= format("\t// %s\n",item.description);
        mstr ~= format("\tstruct %s_Type{\n",item.name);
        // enum addressBlock[]  Block=[]
        if(item.addressBlocks.length > 0){
            mstr ~= format("\t\tenum AddressBlock[]  Block = [");
            foreach(i,block;item.addressBlocks){
                if(i > 0) mstr ~= ",";
                mstr ~= format("AddressBlock(%s, %s, \"%s\")", block.offset, block.size, block.usage);
            }
            mstr ~= format("];\n");
        }
        /// enum interrupt[]  Interrupt=[]
        if(item.interrupts.length > 0){
            mstr ~= format("\t\tenum Interrupt[]  IRQs = [");
            foreach(i,irq;item.interrupts){
                if(i > 0) mstr ~= ",";
                mstr ~= format("Interrupt(\"%s\", \"%s\", %s)", irq.name, irq.description, irq.value);
            }
            mstr ~= format("];\n");
        }

        /// 寄存器
        if(item.registers.length > 0)
        {
            // 排序
            item.registers.sort!(
                (a,b) => a.addressOffset < b.addressOffset
            );

            //最后一个内存地址
            uint lastOffset = 0;

            
            // RO_Reg!(mask_DBGMCU_IDCODE,uint) DBGMCU_IDCODE 寄存器变量声明
            foreach(i,reg;item.registers)
            {
                auto addoffset = 0;
                try
                {
                    addoffset = reg.addressOffset.BigInt().toInt;
                }
                catch (Exception e)
                {
                    errorf("error:%s", reg);
                }

                

                if(addoffset > lastOffset)
                {
                    auto space = (addoffset - lastOffset) / 4; // uint
                    /// 填充空白
                    mstr ~= format("\t\tprivate uint[%d] reserved%d;\t// skip %d bytes\n",space , i,(addoffset - lastOffset));
                }
                //assert(reg.addressOffset.to!uint >= lastOffset);

                {
                    //更新最后地址
                    if(reg.size == "0x20") lastOffset = addoffset + 4;
                    if(reg.size == "0x10") lastOffset = addoffset + 2;
                    if(reg.size == "0x08") lastOffset = addoffset + 1;
                }

                mstr ~= format("\t\t@RegInfo(\"%s\",%s) ", reg.name, reg.addressOffset);
                /*
                if(reg.access == "read-only") mstr ~= "RO_Reg!";
                if(reg.access == "read-write") mstr ~= "RW_Reg!";
                if(reg.access == "write-only") mstr ~= "WO_Reg!";
                */

                switch(reg.access)
                {
                    case "read-only":
                        mstr ~= "RO_Reg!";
                        break;
                    case "write-only":
                        mstr ~= "WO_Reg!";
                        break;
                    default:
                        mstr ~= "RW_Reg!";
                        break;
                }


                mstr ~= format("(mask_%s,", reg.name);

                if(reg.size == "0x20") mstr ~= "uint";
                if(reg.size == "0x10") mstr ~= "ushort";
                if(reg.size == "0x08") mstr ~= "ubyte";
                mstr ~= format(") %s;\t\t // %s\n", reg.name, reg.description);
            }
            // 生成寄存器枚举
            foreach (reg; item.registers)
            {
                mstr ~= format("\t\tenum mask_%s {\n", reg.name);
                foreach(i,field;reg.bitFields)
                {
                    //if(i>0) mstr ~= ",";
                    mstr ~= format("\t\t\t\t%s = makeBitField!uint(%s,%s),\t\t// %s\n", 
                    field.name, 
                    field.bitOffset,
                    field.bitWidth,
                    field.description
                    );
                }

                mstr ~= format("\t\t}\n");
            }
        }

        mstr ~= format("\t}\n");


        if(item.groupName.length > 0){
            groupStr[item.groupName] ~= mstr;
        }else{
            peripheralsStr ~= mstr;
        }
    }

    /// 生成外设组段落
    foreach(k,vgroup;groupStr)
    {
        peripheralsStr ~= format("\t// %s Group \n",k);

        foreach (v; vgroup)
        {
            peripheralsStr ~= v;
        }
    }
    

    return peripheralsStr;
}


/// 建立中断枚举段落
string makeIrqs(Interrupt[] irqMap)
in(irqMap.length > 0)
{

    irqMap.sort!(
        (a,b) => a.value < b.value
    );

    string irqs = "\tenum IRQn {\n";

    foreach(irq;irqMap)
    {
        irqs ~= format("\t\t%s = %s,\t\t// %s\n", irq.name, irq.value, irq.description);
    }
    irqs ~= "\t};\n";

    return irqs;
}


/// 读取外设信息
void readPeripherals(JSONValue svdJson)
in(svdJson.type == JSONType.array)
{
    foreach(JSONValue peri;svdJson.array)
    {

    }
}

/// 整理字符串

/// 外设
struct Peripheral
{
    string name;
    string description;
    string groupName;
    string baseAddress;
    AddressBlock[] addressBlocks;
    Interrupt[] interrupts;
    Register[] registers;
}

/// 地址块
struct AddressBlock
{
    string offset;
    string size;
    string usage;
    string protection;
}

/// 中断
struct Interrupt
{
    string name;
    string description;
    string value;
}


/// 寄存器
struct Register
{
    string name;
    string description;
    string displayName;
    string addressOffset;
    string size;
    string access;
    string resetValue;
    BitField[] bitFields;
}

/// 位域
struct BitField
{
    string name;
    string description;
    string bitOffset;
    string bitWidth;
    string access;
}

T makeBitField(T = uint)(size_t offset, size_t width)
//if(isUnsigned!T)
{
    return (T.max >>> (T.sizeof * 8 - width)) << offset;
}






